package com.douyin.service.impl;

import com.douyin.constant.RedisConstant;
import com.douyin.service.FeedService;
import com.douyin.utils.DateUtil;
import com.douyin.utils.RedisCacheUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class FeedServiceImpl implements FeedService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedisCacheUtil redisCacheUtil;
    @Override
    public void deleteOutBoxFeed(Long userId,Collection<Long> fans,Long videoId) {
        //作者删除视频，需要把作者的粉丝的收件箱中删除改视频
        String keyPrefix = RedisConstant.IN_FOLLOW;
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Long fanId : fans) {
                connection.zRem((keyPrefix + fanId).getBytes(), videoId.toString().getBytes());
            }
            connection.zRem((RedisConstant.OUT_FOLLOW+userId).getBytes(), videoId.toString().getBytes());
            return null;
        });
    }

    @Override
    public void deleteInBoxFeed(Long userId, List<Long> videoIds) {
        //用于取关，删除要取关的人创作的视频
        redisTemplate.opsForZSet().remove(RedisConstant.IN_FOLLOW + userId, videoIds.toArray());
    }

    @Override
    public void updateFollowFeed(Long userId, Collection<Long> followIds) {
        String keyPrefix = RedisConstant.IN_FOLLOW;
        final Date curDate = new Date();
        final Date limitDate = DateUtil.addDateDays(curDate, -3);

        final Set<ZSetOperations.TypedTuple<Long>> set = redisTemplate.opsForZSet().rangeWithScores(keyPrefix + userId, -1, -1);
        if (!ObjectUtils.isEmpty(set)) {
            //获得上次查看关注视频的时间
            Double oldTime = set.iterator().next().getScore();
            init(userId,oldTime.longValue(),new Date().getTime(),followIds);
        } else {
            //从n天前的关注人视频放入收件箱
            init(userId,limitDate.getTime(),curDate.getTime(),followIds);
        }
    }


    @Override
    public void pushOutBoxFeed(Long userId,Long videoId,Long time) {
        //用于作者发布视频，添加到发件箱
        redisCacheUtil.zadd(RedisConstant.OUT_FOLLOW + userId, time, videoId, -1);
    }

    @Override
    public void pushInBoxFeed(Long userId, Long videoId, Long time) {
        //有update收件箱，所以不需要
    }

    public void init(Long userId,Long min,Long max,Collection<Long> followIds) {
        String t1 = RedisConstant.OUT_FOLLOW;
        String t2 = RedisConstant.IN_FOLLOW;
        // 查看每个关注人的发件箱
        final List<Set<DefaultTypedTuple>> result = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Long followId : followIds) {
                //根据时间获取
                connection.zRevRangeByScoreWithScores((t1 + followId).getBytes(), min, max, 0, 50);
            }
            return null;
        });
        final ObjectMapper objectMapper = new ObjectMapper();
        final HashSet<Long> ids = new HashSet<>();
        // 放入收件箱
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Set<DefaultTypedTuple> tuples : result) {
                if (!ObjectUtils.isEmpty(tuples)) {

                    for (DefaultTypedTuple tuple : tuples) {

                        final Object value = tuple.getValue();
                        ids.add(Long.parseLong(value.toString()));
                        final byte[] key = (t2 + userId).getBytes();
                        try {
                            connection.zAdd(key, tuple.getScore(), objectMapper.writeValueAsBytes(value));
                        } catch (JsonProcessingException e) {
                            e.printStackTrace();
                        }
                        connection.expire(key, RedisConstant.HISTORY_TIME);
                    }
                }
            }
            return null;
        });
    }
}
